/*
 * This file is part of the Try! Engine project.
 *
 * Copyright 2010-2011 Emanuele Bertoldi. All rights reserved.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the modified BSD License along with this
 * program. If not, see <http://www.opensource.org/licenses/bsd-license.php>
 */

#include "model3dnode.h"
#include <Horde3DUtils.h>

using namespace Try;

Model3DNode::Model3DNode(
        const String& name,
        const ResourceHandle& model,
        const Transform& t,
        SceneNode* parent
        )
    : SceneNode(h3dAddModelNode(SceneNode::rootNodeHandle(), name.c_str(), model))
{
    this->setParent(parent);
    this->setTransform(t);
}

Model3DNode::Model3DNode(
        const String& filename,
        const Transform& t,
        SceneNode* parent
        )
    : SceneNode(0)
{
    H3DRes res = h3dAddResource(H3DResTypes::SceneGraph, filename.c_str(), 0);

    h3dutLoadResourcesFromDisk("data/");

    NodeHandle n = h3dAddNodes(SceneNode::rootNodeHandle(), res);

    this->setNodeHandle(n);
    this->setParent(parent);
    this->setTransform(t);
}

Model3DNode::~Model3DNode()
{
    this->removeAllAnimations();
}

int Model3DNode::addAnimation(const String& filename, float weight, int layer)
{
    if (m_node)
    {
        int stage = this->animationCount();

        ResourceHandle anim_res = h3dAddResource(H3DResTypes::Animation, filename.c_str(), 0);

        h3dutLoadResourcesFromDisk("data/");

        h3dSetupModelAnimStage(m_node, stage, anim_res, layer, "", false);

        m_animTimes[stage] = 0;
        m_animLayers[stage] = layer;
        m_animWeights[stage] = weight;

        return stage;
    }

    return -1;
}

void Model3DNode::removeAllAnimations()
{
    for (int i=this->animationCount(); i>0; i--)
        this->removeAnimation(i-1);
}

void Model3DNode::removeAnimation(int animation_id)
{
    if (m_animTimes.find(animation_id) != m_animTimes.end())
    {
        m_animTimes.erase(animation_id);
        m_animLayers.erase(animation_id);
        m_animWeights.erase(animation_id);

        // TODO: free animation resource?
    }
}

void Model3DNode::updateAllAnimations(double dt)
{
    for (int i=0; i<this->animationCount(); i++)
        this->updateAnimation(i, dt);
}

void Model3DNode::updateAnimation(int animation_id, double dt)
{
    if (m_animTimes.find(animation_id) != m_animTimes.end())
    {
        float time = m_animTimes.at(animation_id) + dt;
        float weight = m_animWeights.at(animation_id);

        h3dSetModelAnimParams(m_node, animation_id, time, weight);
        h3dUpdateModel(m_node, H3DModelUpdateFlags::Animation | H3DModelUpdateFlags::Geometry);

        m_animTimes[animation_id] = time;
    }
}

void Model3DNode::resetAllAnimations()
{
    for (int i=0; i<this->animationCount(); i++)
        this->resetAnimation(i);
}

void Model3DNode::resetAnimation(int animation_id)
{
    if (m_animTimes.find(animation_id) != m_animTimes.end())
        this->updateAnimation(animation_id, -m_animTimes.at(animation_id));
}

void Model3DNode::changeAnimationWeight(int animation_id, float weight)
{
    if (m_animWeights.find(animation_id) != m_animWeights.end())
        m_animWeights[animation_id] = weight;
}

int Model3DNode::animationCount() const
{
    return m_animTimes.size();
}

float Model3DNode::animationTime(int animation_id) const
{
    if (m_animTimes.find(animation_id) != m_animTimes.end())
        return m_animTimes.at(animation_id);

    return 0;
}

float Model3DNode::animationWeight(int animation_id) const
{
    if (m_animTimes.find(animation_id) != m_animTimes.end())
        return m_animWeights.at(animation_id);

    return 0;
}

